
/**********************************************************************************\
** 文件名称: oled.c
** 创建人员: YCG
** 创建日期: 2018-12-25
** 文档描述:
** 版本代号: V0.1
** 版本说明: 初始版本
\********************************************************************************/
#include "oled.h"

#ifdef I2C_USE_HARDWARE
CacheHead_t OledCache = {NULL, NULL, NULL};  //oled发送数据缓存列表
extern I2C_HandleTypeDef OLED_IIC;           //IIC控制头
__IO static uint8_t OledTxState = 0;         //发送状态，0没有在发送，1正在发送
#endif


static uint8_t* OledBuf;              //显示缓存指令指针
static uint8_t* OledData;             //显示缓存数据指针


/*******************************************************************************
** 函数名称: OLED_Init
** 功能描述: OLED初始化
** 参数说明: None
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2018-12-25
********************************************************************************/
void OLED_Init(void)
{
    OledBuf = MyMalloc(OLED_BUF_SIZE);      //获取缓存空间
    OledBuf[0] = OLED_DATA;                 //表示这是传送数据
    OledData   = OledBuf + 1;               //获取数据存放的地址

#ifdef OLED_RES_Pin   //如果定义了复位引脚
    HAL_GPIO_WritePin(OLED_RES_GPIO_Port, OLED_RES_Pin, GPIO_PIN_SET);
    HAL_Delay(1);
    HAL_GPIO_WritePin(OLED_RES_GPIO_Port, OLED_RES_Pin, GPIO_PIN_RESET);
    HAL_Delay(1);
    HAL_GPIO_WritePin(OLED_RES_GPIO_Port, OLED_RES_Pin, GPIO_PIN_SET);
    HAL_Delay(1);
#endif

    OLED_SendCmd(0xAE); //关闭显示

    OLED_SendCmd(0xD5); //设置显示时钟分频/振荡器频率
    OLED_SendCmd(80);    //[3:0],分频因子;[7:4],震荡频率

    OLED_SendCmd(0xA8); //设置驱动路数(显示屏的行数)
    OLED_SendCmd(0x3F); //设置为64行

    OLED_SendCmd(0xD3); //设置显示偏移量
    OLED_SendCmd(0x0);  //默认为 0
    OLED_SendCmd(0x40); //设置显示开始行 [5:0],行数

    OLED_SendCmd(0x8D); //电荷泵设置
    OLED_SendCmd(0x14); //bit2，开启/关闭

    OLED_SendCmd(0x20); //设置存储器寻址方式
    OLED_SendCmd(0x00); //水平
    //[1:0],00，列地址模式;01，行地址模式;10,页地址模式;默认 10;
    OLED_SendCmd(0xA1); //段重定义设置,bit0:0,0->0;1,0->127;
    OLED_SendCmd(0xC0); //设置 COM 扫描方向;
    //bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动
    OLED_SendCmd(0xda); //设置COM引脚硬件配置
    OLED_SendCmd(0x12); //[5:4]配置

    OLED_SendCmd(0x81); //设置对比度
    OLED_SendCmd(0xFF);

    OLED_SendCmd(0xD9); //设置预充电周期
    OLED_SendCmd(0xF1); //[3:0],PHASE 1;[7:4],PHASE 2
    OLED_SendCmd(0xDB); //设置VCOMH输出级别  亮度
    OLED_SendCmd(0x30); //0.83*VCC
    //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;

    OLED_SendCmd(0xc8); //垂直翻转 C0 C8
//
//    OLED_SendCmd(0x40); //设置显示开始行 [5:0],行数.映射显存的地址
//    OLED_SendCmd(0xb0); //页寻址模式，设置页开始地址
//    OLED_SendCmd(0x00); //
//    OLED_SendCmd(0xa0); //seg re-map 0->127

    OLED_SendCmd(0x21); //设置列
    OLED_SendCmd(0);    //起始地址
    OLED_SendCmd(127);  //结束地址
    OLED_SendCmd(0x22); //设置页
    OLED_SendCmd(0);    //起始地址
    OLED_SendCmd(7);    //结束地址

    OLED_SendCmd(0xA4); //全局显示开启
    OLED_SendCmd(0xA6); //正常显示，A7反相显示
    OLED_SendCmd(0xAF); //开启显示
    OLED_Clear();       //清楚OLED缓存中的数据
    OLED_Update();      //更新显示
}

#ifdef I2C_USE_HARDWARE
/*******************************************************************************
** 函数名称: OLED_IIStart
** 功能描述: 启用II开始发送数据
** 参数说明: None
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-04-30
********************************************************************************/
static void OLED_IIC_Start(void) {
    if(OledTxState == 0) {                     //如果没有正在发送，就启动发送
        OledTxState = 1;                        //发送标志置1，表示正在发送

        CacheItem_t * yCache = CacheRead(&OledCache);   //获取当前的发送数据块
        if(yCache->Size == OLED_CMD_SIZE) {              //判断数据块携带的数据类型为命令
            HAL_I2C_Master_Transmit_DMA(&OLED_IIC, OLED_ADD, CacheGetAdd(yCache), OLED_CMD_SIZE);
        } else if(yCache->Size == OLED_BUF_SIZE) {    //判断数据块携带的数据类型为显存数据
            HAL_I2C_Master_Transmit_DMA(&OLED_IIC, OLED_ADD, OledBuf, OLED_BUF_SIZE);
        }
    }
}
#endif

/*******************************************************************************
** 函数名称: OLED_SendCmd
** 功能描述: OLED发送指令
** 参数说明: cmd: [输入]  指令
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2018-12-25
********************************************************************************/
void OLED_SendCmd(uint8_t cmd)
{

#ifdef I2C_USE_HARDWARE
    CacheItem_t *yCache =  CacheGet(&OledCache, OLED_CMD_SIZE);     //获取缓存空间
    if(yCache != NULL) {

        yCache->Size = OLED_CMD_SIZE;              //设置缓存发送数据的大小
        uint8_t*  CacheAdd = CacheGetAdd(yCache);  //获取缓存中数据的存放地址
        CacheAdd[0] = OLED_CMD;                    //表示这是传送数据
        CacheAdd[1] = cmd;                         //发送的指令

        OLED_IIC_Start();                            //启动IIC进行发送

    }
#else
    IIC_Start();
    IIC_SendByte(OLED_ADD);
    IIC_SendByte(OLED_CMD);
    IIC_SendByte(cmd);
    IIC_Stop();
#endif

}
/*******************************************************************************
** 函数名称: OLED_Update
** 功能描述: 更新显示
** 参数说明: None
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-04-30
********************************************************************************/
void OLED_Update(void) {

    OLED_SetPos(0, 0);
	
#ifdef I2C_USE_HARDWARE
    CacheItem_t *yCache = CacheGet(&OledCache, 8);     //不需要空间缓存空间，只需要维持链表结构
    if(yCache != NULL) {
        yCache->Size = OLED_BUF_SIZE;              //设置缓存发送数据的大小

        OLED_IIC_Start();                            //启动IIC进行发送
    }
#else
	
    uint16_t num;

    IIC_Start();
    IIC_SendByte(OLED_ADD);
    for(num = 0; num < OLED_BUF_SIZE; num++) {
        IIC_SendByte(OledBuf[num]);
    }
    IIC_Stop();
		
#endif
}

#ifdef I2C_USE_HARDWARE
/*******************************************************************************
** 函数名称: OLED_IICCallback
** 功能描述: OLED中断发送完成回调函数
** 参数说明: None
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-04-30
********************************************************************************/
void OLED_IIC_Callback(void) {

    CacheItem_t *yCache = CacheRead(&OledCache);     //获取当前的发送数据块
    if(yCache != NULL) {
        if(yCache->Size == OLED_CMD_SIZE) {          //判断数据块携带的数据类型为命令
            HAL_I2C_Master_Transmit_DMA(&OLED_IIC, OLED_ADD, CacheGetAdd(yCache), OLED_CMD_SIZE);
        } else if(yCache->Size == OLED_BUF_SIZE) {   //判断数据块携带的数据类型为显存数据
            HAL_I2C_Master_Transmit_DMA(&OLED_IIC, OLED_ADD, OledBuf, OLED_BUF_SIZE);
        }
    } else {
        OledTxState = 0;                              //表示没有数据正在发送了
    }
}

void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    if(hi2c == &OLED_IIC) {
        OLED_IIC_Callback();
    }
}
#endif

void OLED_DrawPixel(uint8_t x, uint8_t y, uint8_t color) {

    const uint8_t SetBit[] = {  0x01, 0x02, 0x04, 0x08, 0x10,  0x20, 0x40, 0x80 };
    const uint8_t ClrBit[] = {  0xFE, 0xFD, 0xFB, 0xF7, 0xEF,  0xDF, 0xBF, 0x7F };

    if((x >= OLED_WIDTH) || (y >= OLED_HEIGHT)) return;
    if(color) OledData[x + (y >> 3) * OLED_WIDTH] |= SetBit[y & 7];
    else      OledData[x + (y >> 3) * OLED_WIDTH] &= ClrBit[y & 7];
}

void OLED_DrawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) {
    int16_t steep = abs(y1 - y0) > abs(x1 - x0);
    if (steep) {
        SWAP(x0, y0);
        SWAP(x1, y1);
    }
    if (x0 > x1) {
        SWAP(x0, x1);
        SWAP(y0, y1);
    }
    int16_t dx, dy;
    dx = x1 - x0;
    dy = abs(y1 - y0);

    int16_t err = dx / 2;
    int16_t ystep;

    if (y0 < y1) {
        ystep = 1;
    } else {
        ystep = -1;
    }
    for (; x0 <= x1; x0++) {
        if (steep) {
            OLED_DrawPixel(y0, x0, color);
        } else {
            OLED_DrawPixel(x0, y0, color);
        }
        err -= dy;
        if (err < 0) {
            y0 += ystep;
            err += dx;
        }
    }
}

void OLED_DrawVLine(int16_t x, int16_t y, int16_t h, uint16_t color) {
    OLED_DrawLine(x, y, x, y + h - 1, color);
}

void OLED_DrawHLine(int16_t x, int16_t y, int16_t w, uint16_t color) {
    OLED_DrawLine(x, y, x + w - 1, y, color);
}

void OLED_DrawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) {
    int16_t f = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x = 0;
    int16_t y = r;

    OLED_DrawPixel(x0, y0 + r, color);
    OLED_DrawPixel(x0, y0 - r, color);
    OLED_DrawPixel(x0 + r, y0, color);
    OLED_DrawPixel(x0 - r, y0, color);

    while (x < y) {
        if (f >= 0) {
            y--;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;

        OLED_DrawPixel(x0 + x, y0 + y, color);
        OLED_DrawPixel(x0 - x, y0 + y, color);
        OLED_DrawPixel(x0 + x, y0 - y, color);
        OLED_DrawPixel(x0 - x, y0 - y, color);
        OLED_DrawPixel(x0 + y, y0 + x, color);
        OLED_DrawPixel(x0 - y, y0 + x, color);
        OLED_DrawPixel(x0 + y, y0 - x, color);
        OLED_DrawPixel(x0 - y, y0 - x, color);
    }
}

void OLED_DrawCircleHelper( int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color) {
    int16_t f     = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x     = 0;
    int16_t y     = r;

    while (x < y) {
        if (f >= 0) {
            y--;
            ddF_y += 2;
            f     += ddF_y;
        }
        x++;
        ddF_x += 2;
        f     += ddF_x;
        if (cornername & 0x4) {
            OLED_DrawPixel(x0 + x, y0 + y, color);
            OLED_DrawPixel(x0 + y, y0 + x, color);
        }
        if (cornername & 0x2) {
            OLED_DrawPixel(x0 + x, y0 - y, color);
            OLED_DrawPixel(x0 + y, y0 - x, color);
        }
        if (cornername & 0x8) {
            OLED_DrawPixel(x0 - y, y0 + x, color);
            OLED_DrawPixel(x0 - x, y0 + y, color);
        }
        if (cornername & 0x1) {
            OLED_DrawPixel(x0 - y, y0 - x, color);
            OLED_DrawPixel(x0 - x, y0 - y, color);
        }
    }
}

void OLED_FillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color) {

    int16_t f     = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x     = 0;
    int16_t y     = r;

    while (x < y) {
        if (f >= 0) {
            y--;
            ddF_y += 2;
            f     += ddF_y;
        }
        x++;
        ddF_x += 2;
        f     += ddF_x;

        if (cornername & 0x1) {
            OLED_DrawVLine(x0 + x, y0 - y, 2 * y + 1 + delta, color);
            OLED_DrawVLine(x0 + y, y0 - x, 2 * x + 1 + delta, color);
        }
        if (cornername & 0x2) {
            OLED_DrawVLine(x0 - x, y0 - y, 2 * y + 1 + delta, color);
            OLED_DrawVLine(x0 - y, y0 - x, 2 * x + 1 + delta, color);
        }
    }
}

void OLED_FillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) {
    OLED_DrawVLine(x0, y0 - r, 2 * r + 1, color);
    OLED_FillCircleHelper(x0, y0, r, 3, 0, color);
}
void OLED_DrawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
    OLED_DrawHLine(x, y, w, color);
    OLED_DrawHLine(x, y + h - 1, w, color);
    OLED_DrawVLine(x, y, h, color);
    OLED_DrawVLine(x + w - 1, y, h, color);
}

void OLED_DrawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) {
    // smarter version

    OLED_DrawHLine(x + r, y, w - 2 * r, color); // Top
    OLED_DrawHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom
    OLED_DrawVLine(x, y + r, h - 2 * r, color); // Left
    OLED_DrawVLine(x + w - 1, y + r, h - 2 * r, color); // Right
    // draw four corners
    OLED_DrawCircleHelper(x + r, y + r, r, 1, color);
    OLED_DrawCircleHelper(x + w - r - 1, y + r, r, 2, color);
    OLED_DrawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color);
    OLED_DrawCircleHelper(x + r, y + h - r - 1, r, 8, color);

}
void OLED_FillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) {
    // smarter version
    OLED_FillRect(x + r, y, w - 2 * r, h, color);
    // draw four corners
    OLED_FillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color);
    OLED_FillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color);
}

void OLED_FillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
    for (int16_t i = x; i < x + w; i++) {
        for (int16_t j = y; j < y + h; j++) {
            OLED_DrawPixel(i, j, color);
        }
    }

}

void OLED_DrawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) {
    OLED_DrawLine(x0, y0, x1, y1, color);
    OLED_DrawLine(x1, y1, x2, y2, color);
    OLED_DrawLine(x2, y2, x0, y0, color);
}

void OLED_FillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) {

    int16_t a, b, y, last;

    // Sort coordinates by Y order (y2 >= y1 >= y0)
    if (y0 > y1) {
        SWAP(y0, y1);
        SWAP(x0, x1);
    }
    if (y1 > y2) {
        SWAP(y2, y1);
        SWAP(x2, x1);
    }
    if (y0 > y1) {
        SWAP(y0, y1);
        SWAP(x0, x1);
    }

    if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
        a = b = x0;
        if(x1 < a)      a = x1;
        else if(x1 > b) b = x1;
        if(x2 < a)      a = x2;
        else if(x2 > b) b = x2;
        OLED_DrawHLine(a, y0, b - a + 1, color);
        return;
    }

    int16_t
    dx01 = x1 - x0,
    dy01 = y1 - y0,
    dx02 = x2 - x0,
    dy02 = y2 - y0,
    dx12 = x2 - x1,
    dy12 = y2 - y1;
    int32_t
    sa   = 0,
    sb   = 0;

    // For upper part of triangle, find scanline crossings for segments
    // 0-1 and 0-2.  If y1=y2 (flat-bottomed triangle), the scanline y1
    // is included here (and second loop will be skipped, avoiding a /0
    // error there), otherwise scanline y1 is skipped here and handled
    // in the second loop...which also avoids a /0 error here if y0=y1
    // (flat-topped triangle).
    if(y1 == y2) last = y1;   // Include y1 scanline
    else         last = y1 - 1; // Skip it

    for(y = y0; y <= last; y++) {
        a   = x0 + sa / dy01;
        b   = x0 + sb / dy02;
        sa += dx01;
        sb += dx02;
        /* longhand:
        a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
        b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
        */
        if(a > b) SWAP(a, b);
        OLED_DrawHLine(a, y, b - a + 1, color);
    }

    // For lower part of triangle, find scanline crossings for segments
    // 0-2 and 1-2.  This loop is skipped if y1=y2.
    sa = dx12 * (y - y1);
    sb = dx02 * (y - y0);
    for(; y <= y2; y++) {
        a   = x1 + sa / dy12;
        b   = x0 + sb / dy02;
        sa += dx12;
        sb += dx02;
        /* longhand:
        a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
        b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
        */
        if(a > b) SWAP(a, b);
        OLED_DrawHLine(a, y, b - a + 1, color);
    }
}

void OLED_DrawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color) {

    uint8_t byte = 0;
    for(int16_t i = 0; i < w; i++, x++) {
        for(int16_t j = 0; j < h; j++) {
            if(j & 7) byte >>= 1;
            else      byte   = bitmap[(j / 8) * OLED_WIDTH + i];
            if(byte & 0x01) OLED_DrawPixel(x, y + j, color);
        }
    }
}

void OLED_DrawChar(int16_t x, int16_t y, uint8_t c,  uint16_t color) {

    if(color) OLED_FillRect(x, y, 6, 8, 0);
    else OLED_FillRect(x, y, 6, 8, 1);
    OLED_DrawBitmap(x, y, F6x8[c - ' '], 6, 8, color);
}

uint8_t Print_X = 0, Print_Y = 0;

void OLED_SendChar(uint8_t c) {
    if(c == '\n') {
        Print_X = 0;
        Print_Y = (Print_Y + 8) % OLED_HEIGHT; //换行
    } else {
        OLED_DrawChar(Print_X, Print_Y, c, 1);
        Print_X += 6;
        if(Print_X > (OLED_WIDTH - 6)) {
            Print_X = 0;
            Print_Y = (Print_Y + 8) % OLED_HEIGHT; //换行
        }
    }
}

void OLED_Printf(int16_t x, int16_t y, char *fmt, ...)
{
    va_list arp;
    va_start (arp, fmt); // 将可变长参数转换为va_list
    Print_X = x;
    Print_Y = y;
    Print(OLED_SendChar, fmt, arp);
    va_end (arp);
}

void OLED_UPrintf(int16_t x, int16_t y, char *fmt, ...)
{
    va_list arp;
    va_start (arp, fmt); // 将可变长参数转换为va_list
    Print_X = x;
    Print_Y = y;
    Print(OLED_SendChar, fmt, arp);
    OLED_Update();
    va_end (arp);
}

/*******************************************************************************
** 函数名称: OLED_SetPos
** 功能描述: OLED设置显示位置
** 参数说明: x: [输入/出]
**			 y: [输入/出]
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2018-12-25
********************************************************************************/
void OLED_SetPos(uint8_t x, uint8_t y)
{
    OLED_SendCmd(0xb0 + y);
    OLED_SendCmd(((x & 0xf0) >> 4) | 0x10);
    OLED_SendCmd((x & 0x0f) | 0x01);
}

/*******************************************************************************
** 函数名称: OLED_Clear
** 功能描述: OLED清屏
** 参数说明:
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2018-12-25
********************************************************************************/
void OLED_Clear(void)
{
    for(uint32_t i = 1; i < (OLED_BUF_SIZE); i++)
        OledData[i] = 0 ;
}
/********************************End of File************************************/

